/*
 *  SF3KtoProT - Converts Star Fighter 3000 music to Amiga ProTracker format
 *  ProTracker conversion routines
 *  Copyright (C) 2009  Chris Bazley
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public Licence as published by
 *  the Free Software Foundation; either version 2 of the Licence, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public Licence for more details.
 *
 *  You should have received a copy of the GNU General Public Licence
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* ISO library header files */
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#include <inttypes.h>
#include <limits.h>

/* Local header files */
#include "main.h"
#include "samp.h"
#include "protracker.h"
#include "platform.h"

#define INIT_SIZE            4
#define SEMITONES_PER_OCTAVE 12
#define SECONDS_PER_MINUTE   60

/* The following values are dictated by the SF3000 music file format */
#define MAX_SF_PATTERNS        64
#define SF_CLOCK_FREQ          90 /* Hz (actually 100, but for latency) */
#define SF_MAX_VOLUME          15
#define SF_MAX_REPEATS         15
#define SF_GLISSANDO_THRESHOLD 2 /* Values below this mean 'play note' */
#define SF_TUNING_OCTAVE       4096 /* Tuning units per octave */
#define NUM_SF_CHANNELS        4

/* The following values are dictated by the ProTracker file format */
#define MAX_PT_SAMPLES         31
#define BYTES_PER_PT_SAMPLE    30
#define MAX_PT_SONG_LEN        128
#define MAX_PT_POSITIONS       64
#define NUM_SF_DIVISIONS       64
#define NUM_PT_CHANNELS        4
#define PT_BPM_DIVISOR         24
#define PT_SPEED_THRESHOLD     32
#define PT_MAX_VOLUME          64
#define PT_COM_NORMAL          0x0
#define PT_COM_TONE_PORTAMENTO 0x3
#define PT_COM_SET_VOLUME      0xc
#define PT_COM_PATTERN_BREAK   0xd
#define PT_COM_SET_SPEED       0xf
#define PT_TUNING_SEMITONE     8 /* Tuning units per semitone */
#define PT_OCTAVE_RANGE        5

#ifdef NDEBUG
#define DEBUG_OUT if (0) printf
#else /* NDEBUG */
#define DEBUG_OUT if (1) printf
#endif /* NDEBUG */

typedef struct {
  unsigned char  num_repeats;
  unsigned char  sample_num;
  unsigned short half_len;
  unsigned short half_repeat_offset;
  unsigned short half_repeat_len;
  signed long    pt_tuning;
  signed int     octaves_cheat;
} PTSampleInfo;

typedef struct {
  unsigned char note;
  unsigned char oct_vol;
  unsigned char voice_act;
  unsigned char num_repeats;
} SFChannelData;

typedef struct {
  SFChannelData channels[NUM_SF_CHANNELS];
} SFDivision;

typedef struct {
  SFDivision divisions[NUM_SF_DIVISIONS];
} SFPattern;

typedef enum {
  GlissandoState_None, /* No glissando on this channel since last note */
  GlissandoState_Start, /* First event during a glissando */
  GlissandoState_Wait, /* Second, fourth, sixth event... etc. */
  GlissandoState_Continue /* Third, fifth, seventh event... etc. */
} GlissandoState;

typedef struct {
  unsigned char  pt_sample_no;
  unsigned char  sample_num;
  unsigned short target_pitch;
  GlissandoState glissando_state;
} ChannelState;

static unsigned int get_pt_period(unsigned int octave, unsigned int note)
{
  /* Period table for Tuning 0, normal. Octaves 0 and 4 are non-standard and
     may not be supported by a tracker player. */
  static const unsigned int period_table[PT_OCTAVE_RANGE][SEMITONES_PER_OCTAVE] =
  {
    {1712,1616,1525,1440,1357,1281,1209,1141,1077,1017,961,907},/* C-0 to B-0 */
    {856,808,762,720,678,640,604,570,538,508,480,453}, /* C-1 to B-1 */
    {428,404,381,360,339,320,302,285,269,254,240,226}, /* C-2 to B-2 */
    {214,202,190,180,170,160,151,143,135,127,120,113}, /* C-3 to B-3 */
    {107,101,95,90,85,80,76,71,67,64,60,57} /* C-4 to B-4 */
  };

  assert(octave < PT_OCTAVE_RANGE);
  assert(note <= SEMITONES_PER_OCTAVE);

  return period_table[octave][note];
}

static bool fput_pt_command(unsigned int  pt_effect_com,
                            unsigned int  pt_effect_val,
                            unsigned int  pt_sample_no,
                            unsigned int  pt_period,
                            FILE         *f)
{
  assert(pt_effect_com <= 15);
  assert(pt_effect_val <= UCHAR_MAX);
  assert(pt_sample_no <= MAX_PT_SAMPLES);
  assert(pt_period <= 0xfff);
  assert(f != NULL);

  /* Write higher 4 bits of note period and sample number */
  if (fputc((pt_period >> 8 & 0xf) | pt_sample_no >> 4 & 0xf, f) == EOF)
    return false; /* failure */

  /* Write lower 8 bits of note period */
  if (fputc(pt_period & UCHAR_MAX, f) == EOF)
    return false; /* failure */

  /* Write effect command number and lower 4 bits of sample number */
  if (fputc((pt_effect_com & 0xf) | (pt_sample_no & 0xf) << 4, f) == EOF)
    return false; /* failure */

  /* Write effect value */
  if (fputc(pt_effect_val, f) == EOF)
    return false; /* failure */

  return true; /* success */
}

static bool fput_halfword(unsigned int halfword, FILE *f)
{
  assert(halfword <= USHRT_MAX);
  assert(f != NULL);

  /* All half-word values in a ProTracker file are big-endian */
  return fputc((halfword >> 8) & UCHAR_MAX, f) != EOF &&
         fputc(halfword & UCHAR_MAX, f) != EOF;
}

static bool command(const SFChannelData *com)
{
  assert(com != NULL);
  return com->note != 0 || com->oct_vol != 0 || com->voice_act != 0 ||
         com->num_repeats != 0;
}

static bool write_sample_table(unsigned int        flags,
                               const PTSampleInfo *pt_samples,
                               unsigned int        num_pt_samples,
                               const SampleInfo   *samples_index,
                               FILE               *f)
{
  unsigned int n, pt_sample_no;

  assert(pt_samples != NULL);
  assert(samples_index != NULL);
  assert(f != NULL);

  for (pt_sample_no = 0; pt_sample_no < num_pt_samples; pt_sample_no++) {
    const PTSampleInfo *ptsi;
    const SampleInfo *sample;
    char sample_name[22];
    signed long finetune;

    ptsi = pt_samples + pt_sample_no;
    sample = samples_index + ptsi->sample_num;

    /* Write sample name, padded with null bytes */
    memset(sample_name, '\0', sizeof(sample_name));
    sprintf(sample_name, "%s-R%u-O%d", sample->file_name, ptsi->num_repeats,
            ptsi->octaves_cheat);

    if ((flags & FLAGS_VERBOSE) != 0)
      printf("Writing ProTracker sample table entry %u ('%s')\n",
             pt_sample_no, sample_name);

    fwrite(sample_name, sizeof(sample_name), 1, f);

    /* Write len of sample data DIV 2. */
    if (!fput_halfword(ptsi->half_len, f))
      return false; /* failure */

    /* Convert the ProTracker tuning value to semitones (coarsen it). */
    finetune = ptsi->pt_tuning / PT_TUNING_SEMITONE;

    /* Find the fractional remainder that was discarded by the integer
       division above, and use that as the 'finetune' value. */
    assert(finetune <= LONG_MAX / PT_TUNING_SEMITONE);
    assert(finetune >= LONG_MIN / PT_TUNING_SEMITONE);
    finetune = ptsi->pt_tuning - finetune * PT_TUNING_SEMITONE;

    /* Write signed 8 bit 'finetune' value for sample
       -8 means 1 semitone lower. 7 means 0.875 semitone higher. */
    assert(finetune >= SCHAR_MIN);
    assert(finetune <= SCHAR_MAX);
    if (fputc((int)finetune, f) == EOF)
      return false; /* failure */

    /* Write volume for sample */
    if (fputc(PT_MAX_VOLUME, f) == EOF)
      return false; /* failure */

    /* Write repeat offset DIV 2 */
    if (!fput_halfword(ptsi->half_repeat_offset, f))
      return false; /* failure */

    /* Write repeat len DIV 2 */
    if (!fput_halfword(ptsi->half_repeat_len, f))
      return false; /* failure */
  }

  /* The ProTracker file format allocates a fixed amount of space for the
     sample table, so we must pad it to the required size. */
  assert(num_pt_samples <= MAX_PT_SAMPLES);
  for (n = (MAX_PT_SAMPLES - num_pt_samples) * BYTES_PER_PT_SAMPLE;
       n != 0;
       n--)
  {
    if (fputc(0, f) == EOF)
      return false; /* failure */
  }

  return true; /* success */
}

static bool integrate_samples(unsigned int        flags,
                              const PTSampleInfo *pt_samples,
                              unsigned int        num_pt_samples,
                              const SampleInfo   *samples_index,
                              const char         *samples_dir,
                              const char         *output_file,
                              FILE               *f)
{
  FILE *sample_handle = NULL;
  char *sample_path = NULL;
  bool success = false; /* pessimistic */
  unsigned int pt_sample_no;

  assert(pt_samples != NULL);
  assert(samples_index != NULL);
  assert(samples_dir != NULL);
  assert(output_file != NULL);
  assert(f != NULL);

  for (pt_sample_no = 0; pt_sample_no < num_pt_samples; pt_sample_no++) {
    const PTSampleInfo *ptsi;
    const SampleInfo *sample;
    long int out_count;
    unsigned int num_repeats, repeat;

    if ((flags & FLAGS_VERBOSE) != 0)
      printf("About to write data for ProTracker sample %u\n", pt_sample_no+1);

    ptsi = pt_samples + pt_sample_no;
    sample = samples_index + ptsi->sample_num;

    /* Construct full path name of sample data file */
    sample_path = append_leaf(samples_dir, sample->file_name);
    if (sample_path == NULL) {
      fprintf(stderr,"Failed to allocate memory for sample data file path\n");
      goto cleanup;
    }

    /* Open the sample data file */
    if ((flags & FLAGS_VERBOSE) != 0)
      printf("Opening sample data file '%s'\n", sample_path);

    sample_handle = fopen(sample_path, "rb"); /* open binary for reading */
    if (sample_handle == NULL) {
      fprintf(stderr, "Failed to open sample data file '%s'\n", sample_path);
      goto cleanup;
    }

    /* Initialise the output counter to one less than the defined sample
       len (because termination check is inclusive of zero). */
    out_count = (long int)ptsi->half_len * 2 - 1;

    num_repeats = ptsi->num_repeats;
    if (num_repeats == SF_MAX_REPEATS)
      num_repeats = 0; /* unlimited repeats will be handled automatically */

    for (repeat = 0; repeat <= num_repeats; repeat++) {
      /* If we are looping the sample data then apply the repeat offset to
         prevent repeating the attack phase of the note. */
      if (repeat != 0) {
        if ((flags & FLAGS_VERBOSE) != 0)
          printf("Seeking repeat offset %ld in sample data file\n",
                 (long int)sample->repeat_offset * 2);

        if (fseek(sample_handle,
                  (long int)sample->repeat_offset * 2,
                  SEEK_SET)) {
          fprintf(stderr, "Failed to loop sample data file '%s' at %u\n",
                          sample_path, sample->repeat_offset * 2);
          goto cleanup;
        }
      }

      /* Must copy exactly the defined number of bytes, regardless of whether
         or not we are manually looping the sample data. */
      if ((flags & FLAGS_VERBOSE) != 0)
        printf("About to copy %ld bytes from sample data file\n",
               out_count + 1);

      for (;out_count >= 0; out_count--) {
        /* Read a 16 bit little-endian value but discard the least
           significant 8 bits. */
        int s, dup, pow;
        if (fgetc(sample_handle) == EOF ||
            (s = fgetc(sample_handle)) == EOF)
        {
          if (feof(sample_handle))
            break; /* End of sample (not an error) */

          fprintf(stderr, "Failed reading from sample data file '%s'\n",
                          sample_path);
          goto cleanup;
        }

        /* Create a lower pitched version of a sound by doubling or quadrupling
           each sample. */
        dup = 1;
        for (pow = ptsi->octaves_cheat; pow < 0; pow ++)
          dup *= 2;

        /* Copy most significant byte of the sample to the ProTracker file. */
        for (;dup > 0; dup --) {
          if (fputc(s, f) == EOF) {
            fprintf(stderr, "Failed writing to output file '%s'\n",
                            output_file);
            goto cleanup;
          }
        }

        if (ptsi->octaves_cheat > 0) {
          long int skip;

          /* Resampling interval is 2 to the power of the number of octaves by
             which to transpose upwards. */
          skip = 1;
          for (pow = ptsi->octaves_cheat; pow > 0; pow --)
            skip *= 2;

          skip -= 1; /* the file pointer has already advanced by one sample */
          if (fseek(sample_handle, skip * 2 /* 16 bit samples */, SEEK_CUR)) {
            if (feof(sample_handle))
              break; /* End of sample (not an error) */

            fprintf(stderr, "Failed seeking within sample data file '%s'\n",
                            sample_path);
            goto cleanup;
          }
        }
      }
    }

    /* Close the sample data file */
    if ((flags & FLAGS_VERBOSE) != 0)
      puts("Closing sample data file");

    fclose(sample_handle);
    sample_handle = NULL;

    /* Discard the full path name of the sample data file */
    free(sample_path);
    sample_path = NULL;
  }

  success = true;

cleanup:
  if (sample_handle != NULL) {
    if ((flags & FLAGS_VERBOSE) != 0)
      puts("Closing sample data file");

    fclose(sample_handle);
  }

  free(sample_path);
  return success;
}

static signed int note_to_pt(const SFChannelData *com,
                             unsigned int        *note_out,
                             signed long          semitone_tuning)
{
  /* Convert the SF3000 octave and note numbers into ProTracker format.
     This function may return an out-of-range octave number because the
     ProTracker format is more restrictive. */
  signed long note;
  signed int octave;

  assert(com != NULL);
  octave = (com->oct_vol & 0xf) - 1;
  note = com->note & 0xfl;
  note += semitone_tuning;
  while (note < 0) {
    octave --;
    note += SEMITONES_PER_OCTAVE;
  }
  while (note >= SEMITONES_PER_OCTAVE) {
    octave ++;
    note -= SEMITONES_PER_OCTAVE;
  }
  assert(note >= 0);
  assert(note < SEMITONES_PER_OCTAVE);
  if (note_out != NULL)
    *note_out = (unsigned int)note;

  return octave;
}

static bool make_pt_sample(PTSampleInfo     *ptsi,
                           const SampleInfo *sample,
                           unsigned int      num_repeats,
                           signed int        octaves_cheat)
{
  unsigned long repeat_len, sample_len, repeat_offset;
  int pow;

  assert(ptsi != NULL);
  assert(sample != NULL);

  ptsi->num_repeats = num_repeats;
  ptsi->octaves_cheat = octaves_cheat;

  /* The resolution of the sample data will be reduced from 8 to 16 bits. */
  sample_len = sample->len / 2;
  DEBUG_OUT("Sample len: %lu\n", sample_len);

  repeat_offset = sample->repeat_offset; /* in sample frames not bytes */
  DEBUG_OUT("Repeat offset: %lu\n", repeat_offset);

  /* If we pre-tune the sample to lower or raise the pitch then that will
     affect the length and repeat offset. */
  for (pow = ptsi->octaves_cheat; pow < 0; pow++) {
    assert(repeat_offset <= ULONG_MAX / 2);
    repeat_offset *= 2;

    assert(sample_len <= ULONG_MAX / 2);
    sample_len *= 2;
  }
  for (pow = ptsi->octaves_cheat; pow > 0; pow--) {
    repeat_offset /= 2;
    sample_len /= 2;
  }

  /* ProTracker isn't capable of representing odd sample lengths or offsets
     within a sample (only multiples of 2). */
  repeat_offset /= 2;
  sample_len /= 2;

  DEBUG_OUT("Revised sample len: %lu\n", sample_len);
  DEBUG_OUT("Revised offset: %lu\n", repeat_offset);

  num_repeats = ptsi->num_repeats;
  if (num_repeats == SF_MAX_REPEATS) {
    /* Calculate repeat length */
    repeat_len = sample_len - repeat_offset;
  } else {
    if (num_repeats != 0) {
      /* There is no facility in ProTracker to loop a sample a specific
         number of times, so we must repeat the sample data! */
      DEBUG_OUT("Loop size: %ld\n", sample_len - repeat_offset);
      sample_len += (sample_len - repeat_offset) * num_repeats;
    }
    /* No repeats for this variant of the sample */
    repeat_offset = 0;
    repeat_len = 0;
  }

  /* Validate sample length */
  if (sample_len > USHRT_MAX) {
    fprintf(stderr, "Sample data file '%s' is too long with %u repeats "
                    "from offset %u (when pre-tuned by %d octaves)\n",
                    sample->file_name, num_repeats, sample->repeat_offset,
                    ptsi->octaves_cheat);
    return false; /* failure */
  }

  assert(repeat_len <= sample_len);
  assert(repeat_len <= USHRT_MAX);
  ptsi->half_repeat_len = (unsigned short)repeat_len;

  assert(sample_len <= USHRT_MAX);
  ptsi->half_len = (unsigned short)sample_len;

  assert(repeat_offset < sample_len);
  assert(repeat_offset <= USHRT_MAX);
  ptsi->half_repeat_offset = (unsigned short)repeat_offset;

  return true; /* success */
}

static signed int calc_octaves_cheat(unsigned int         flags,
                                     const SFChannelData *com,
                                     signed long          pt_tuning,
                                     unsigned int        *octave_out,
                                     unsigned int        *note_out)
{
  signed int octave, octaves_cheat, min_octave, max_octave;

  assert(com != NULL);

  /* Convert the SF3000 octave and note numbers into their ProTracker
     equivalents. */
  octave = note_to_pt(com, note_out, pt_tuning / PT_TUNING_SEMITONE);

  /* ProTracker octaves 0 and 4 are non-standard and may not be available. */
  if ((flags & FLAGS_EXTRA_OCTAVES) == 0) {
    min_octave = 1;
    max_octave = PT_OCTAVE_RANGE - 2;
  } else {
    min_octave = 0;
    max_octave = PT_OCTAVE_RANGE - 1;
  }

  if (octave < min_octave) {
    DEBUG_OUT("Invalid octave %d; must pre-tune sample down\n", octave);
    octaves_cheat = octave - min_octave;
    octave = min_octave;
  } else if (octave > max_octave) {
    DEBUG_OUT("Invalid octave %d; must pre-tune sample up\n", octave);
    octaves_cheat = octave - max_octave;
    octave = max_octave;
  } else {
    octaves_cheat = 0;
  }
  if (octave_out != NULL)
    *octave_out = octave;

  return octaves_cheat;
}

static unsigned int find_song_len(const void *music_data)
{
  unsigned int song_len;
  const unsigned char *play_order;

  assert(music_data != NULL);
  play_order = (const unsigned char *)music_data + 40;

  /* There is no record of the song length in a SF3000 music file, so
     iterate through the play order in search of the terminator. */
  for (song_len = 0; song_len < MAX_SF_PATTERNS; song_len++) {
    /* Is this the end of the play list? */
    if (play_order[song_len] == 255)
      break; /* found the terminator */
  }

  return song_len;
}

static bool write_play_order(unsigned int  flags,
                             const char   *output_file,
                             const void   *music_data,
                             unsigned int *last_play,
                             FILE         *f)
{
  const unsigned char *play_order;
  unsigned int pos, song_len, pt_song_len;

  assert(output_file != NULL);
  assert(music_data != NULL);
  assert(f != NULL);

  play_order = (const unsigned char *)music_data + 40;

  /* Find the number of song positions in the SF3000 play order. */
  song_len = find_song_len(music_data);
  if (song_len >= MAX_SF_PATTERNS) {
    fprintf(stderr, "Unterminated pattern play order in input file\n");
    return false; /* failure */
  }
  if ((flags & FLAGS_VERBOSE) != 0)
    printf("SF3000 pattern play order has length %u\n", song_len);

  if (last_play != NULL)
    *last_play = play_order[song_len - 1];

  /* One extra song position will be required to set the tempo and optionally
     another to allow late notes to decay. */
  pt_song_len = song_len + 1;
  if ((flags & FLAGS_BLANK_PATTERN) != 0)
    pt_song_len ++;

  /* Compare the hardwired limits first to avoid checking the actual song
     len unless unavoidable. Here, song_len may equal MAX_SF_PATTERNS. */
  if (MAX_SF_PATTERNS > MAX_PT_SONG_LEN) {
    if (pt_song_len > MAX_PT_SONG_LEN) {
      fprintf(stderr, "Too many patterns to be played in input file\n");
      return false; /* failure */
    }
  }

  /* Write the song length */
  if ((flags & FLAGS_VERBOSE) != 0)
    printf("Writing ProTracker song length %u\n", pt_song_len);
  if (fputc(pt_song_len, f) == EOF)
    goto cleanup_write;

  /* Apparently this byte must be 127 so that old trackers search through all
     patterns when loading. */
  if (fputc(127, f) == EOF)
    goto cleanup_write;

  if ((flags & FLAGS_VERBOSE) != 0)
    printf("Writing ProTracker song positions: 0 (tempo)");

  /* An extra song position (pattern 0) will be required to set the tempo. */
  if (fputc(0, f) == EOF)
    goto cleanup_write;

  /* Write the song positions that dictate the play order for patterns
     ('patterns' in SF3000 parlance). */
  for (pos = 0; pos < song_len; pos++) {
    /* Pattern numbers are offset by 1 because pattern 0 will set the tempo. */
    int pattern = play_order[pos] + 1;

    if ((flags & FLAGS_VERBOSE) != 0)
      printf(",%d", pattern);

    if (fputc(pattern, f) == EOF)
      goto cleanup_write;
  }

  /* An extra song position may be required to allow late notes to finish. */
  if ((flags & FLAGS_BLANK_PATTERN) != 0) {
    int_least32_t extra_pattern = read_int32le((char *)music_data + 32) + 2;

    if ((flags & FLAGS_VERBOSE) != 0)
      printf(",%"PRIdLEAST32" (blank)", extra_pattern);

    assert(extra_pattern <= UCHAR_MAX);
    if (fputc(extra_pattern, f) == EOF)
      goto cleanup_write;
    pos++;
  }

  if ((flags & FLAGS_VERBOSE) != 0)
    puts("");

  /* The ProTracker file format allocates a fixed amount of space for the
     song positions, so we must pad it to the required size. We have already
     written one more position (pattern 0) than 'pos' reflects. */
  for (;pos < MAX_PT_SONG_LEN - 1; pos++) {
    if (fputc(0, f) == EOF)
      goto cleanup_write;
  }
  return true; /* success */

cleanup_write:
  fprintf(stderr, "Failed writing to output file '%s'\n", output_file);
  return false; /* failure */
}

static bool write_tempo_pattern(unsigned int  flags,
                                unsigned int  speed,
                                FILE         *f)
{
  /* ProTracker's representation of tempo is based upon 1/24th of the no. of
     ticks per minute of a 50Hz timer. Star Fighter 3000's music player is
     instead based on a 100 Hz clock and therefore the default ProTracker
     tempo of 125 is too slow. */
  const unsigned int tempo = (SECONDS_PER_MINUTE * SF_CLOCK_FREQ) /
                             PT_BPM_DIVISOR;
  int n;

  assert(speed < PT_SPEED_THRESHOLD); /* higher values set tempo */
  assert(f != NULL);

  if ((flags & FLAGS_VERBOSE) != 0)
    printf("Writing ProTracker pattern to set tempo %u and speed %u\n",
           tempo, speed);

  /* Write a command to set the tempo. */
  assert(tempo >= PT_SPEED_THRESHOLD); /* lower values set speed */
  if (!fput_pt_command(PT_COM_SET_SPEED, tempo, 0, 0, f))
    return false; /* failure */

  /* Write a command to set the speed (i.e. multiplier for the base tempo to
     get the period between playing each position in the patterns). */
  if (!fput_pt_command(PT_COM_SET_SPEED, speed, 0, 0, f))
    return false; /* failure */

  /* Write a command to skip the rest of this pattern. */
  if (!fput_pt_command(PT_COM_PATTERN_BREAK, 0, 0, 0, f))
    return false; /* failure */

  /* The ProTracker file format allocates a fixed amount of space for each
     pattern, so we must pad pattern 0 to the required size. */
  for (n = (MAX_PT_POSITIONS - 1) * NUM_PT_CHANNELS; n >= 0; n--) {
    if (!fput_pt_command(PT_COM_NORMAL, 0, 0, 0, f))
      return false; /* failure */
  }

  return true; /* success */
}

static unsigned int find_pt_sample(const PTSampleInfo *pt_samples,
                                   unsigned int        num_pt_samples,
                                   unsigned int        num_repeats,
                                   unsigned int        sample_num,
                                   signed int          octaves_cheat)
{
  if (pt_samples != NULL) {
    unsigned int pt_sample_no;
    for (pt_sample_no = 0; pt_sample_no < num_pt_samples; pt_sample_no++)
    {
      const PTSampleInfo *ptsi = pt_samples + pt_sample_no;
      if (ptsi->num_repeats == num_repeats &&
          ptsi->sample_num == sample_num &&
          ptsi->octaves_cheat == octaves_cheat)
        return pt_sample_no + 1; /* ProTracker sample numbers are based at 1 */
    }
  }
  return 0; /* no matching ProTracker sample */
}

static signed long sf_to_pt_tuning(signed int sf_tuning)
{
  const int pt_octave = PT_TUNING_SEMITONE * SEMITONES_PER_OCTAVE;
  signed int round;

  assert(check_tuning(sf_tuning));
  round = (sf_tuning >= 0 ? SF_TUNING_OCTAVE / 2 : -(SF_TUNING_OCTAVE / 2));
  return ((long)sf_tuning * pt_octave + round) / SF_TUNING_OCTAVE;
}

static PTSampleInfo *make_pt_sample_list(unsigned int      flags,
                                         const void       *music_data,
                                         const SampleInfo *samples_index,
                                         unsigned int      nsamp,
                                         unsigned int     *num_samples_out)
{
  const unsigned char *voice_table;
  const SFPattern *patterns;
  int_least32_t pattern_no, last_pattern_no;
  unsigned int num_pt_samples, pt_samples_limit;
  PTSampleInfo *pt_samples;

  assert(music_data != NULL);
  assert(samples_index != NULL || nsamp == 0);
  assert(num_samples_out != NULL);

  num_pt_samples = 0;
  pt_samples_limit = 0;
  pt_samples = NULL;

  voice_table = (const unsigned char *)music_data + 16;
  patterns = (SFPattern *)((char *)music_data + 104);
  last_pattern_no = read_int32le((char *)music_data + 32);

  if ((flags & FLAGS_VERBOSE) != 0) {
    unsigned int v;
    puts("SF3000 voice table:");
    for (v = 0; v < 16; v++)
      printf("  %u maps to sample %u\n", v, voice_table[v]);

    printf("SF3000 music comprises %"PRIdLEAST32" patterns\n",
           last_pattern_no + 1);
  }

  for (pattern_no = 0; pattern_no <= last_pattern_no; pattern_no++)
  {
    const SFPattern *pattern;
    unsigned int division_no;

    if ((flags & FLAGS_VERBOSE) != 0)
      printf("About to pre-scan pattern %"PRIdLEAST32"\n", pattern_no);

    pattern = patterns + pattern_no;

    for (division_no = 0; division_no < NUM_SF_DIVISIONS; division_no++)
    {
      unsigned int c;
      const SFDivision *division = &pattern->divisions[division_no];

      assert(NUM_PT_CHANNELS <= NUM_SF_CHANNELS);
      for (c = 0; c < NUM_SF_CHANNELS; c++) {
        unsigned int sample_num, num_repeats;
        const SFChannelData *com = &division->channels[c];
        PTSampleInfo *ptsi;
        const SampleInfo *sample;
        signed long pt_tuning;
        signed int octaves_cheat;

        if (!command(com))
          continue; /* No command here. */

        /* Only interested in note-playing actions, for now. */
        if (com->voice_act >> 4 >= SF_GLISSANDO_THRESHOLD)
          continue;

        /* Decode the voice number into a sample number */
        sample_num = voice_table[com->voice_act & 0xf];
        sample = samples_index + sample_num;
        if (sample_num >= nsamp || sample->type == SampleInfo_Type_Unused) {
          printf("%d %d %d\n", nsamp, sample_num, sample->type);
          fprintf(stderr, "Warning: Sample number %u is not defined!\n",
                          sample_num);
          continue;
        }

        if (sample->type == SampleInfo_Type_Effect) {
          if ((flags & FLAGS_VERBOSE) != 0) {
            printf("Sound effect on channel %u is %s (division %u of pattern %"
                   PRIdLEAST32")\n",
                   c + 1,
                   (flags & FLAGS_ALLOW_SFX) != 0 ? "allowed" : "forbidden",
                   division_no,
                   pattern_no);
          }
          if ((flags & FLAGS_ALLOW_SFX) == 0)
            continue; /* Sound effects not allowed during music */
        } else {
          assert(sample->type == SampleInfo_Type_Music);
        }

        /* Decode the number of repeats */
        num_repeats = com->num_repeats >> 4;

        /* Calculate the equivalent tuning value in ProTracker units
           (-8 means 1 semitone lower. 7 means 0.875 semitone higher) */
        pt_tuning = sf_to_pt_tuning(sample->tuning);
        octaves_cheat = calc_octaves_cheat(flags, com, pt_tuning, NULL, NULL);

        /* If no usable variation of the sample required for this note
           already exists then invent one. */
        if (find_pt_sample(pt_samples,
                           num_pt_samples,
                           num_repeats,
                           sample_num,
                           octaves_cheat) == 0) {
          if (num_pt_samples >= MAX_PT_SAMPLES) {
            fprintf(stderr, "Song requires too many ProTracker samples "
                            "(limit is %u)\n", MAX_PT_SAMPLES);
            goto cleanup;
          }
          if (num_pt_samples >= pt_samples_limit) {
            /* (Re-)allocate buffer for ProTracker samples array */
            size_t        new_size;
            PTSampleInfo *new_buf;

            if (pt_samples_limit == 0)
              pt_samples_limit = INIT_SIZE;
            else
              pt_samples_limit *= 2;

            new_size = pt_samples_limit * sizeof(*pt_samples);
            new_buf = realloc(pt_samples, new_size);
            if (new_buf == NULL) {
              fprintf(stderr, "Failed to allocate %u bytes for ProTracker "
                              "sample info\n", new_size);
              goto cleanup;
            }
            pt_samples = new_buf;
          }

          /* Populate the next element of the ProTracker samples array */
          ptsi = pt_samples + num_pt_samples;
          ptsi->sample_num = sample_num;
          ptsi->pt_tuning = pt_tuning;
          if (!make_pt_sample(ptsi,
                              sample,
                              num_repeats,
                              octaves_cheat))
            goto cleanup;

          num_pt_samples++;
          if ((flags & FLAGS_VERBOSE) != 0) {
            printf("ProTracker sample %u will be:\n", num_pt_samples);

            printf("  %u repeats of sample %u ('%s'), "
                   "pre-tuned up by %d octaves\n", ptsi->num_repeats,
                   ptsi->sample_num, sample->file_name, ptsi->octaves_cheat);

            printf("  Tuning:%ld Length: %u Repeat offset:%u "
                   "Repeat length:%u\n", ptsi->pt_tuning, ptsi->half_len * 2,
                   ptsi->half_repeat_offset * 2, ptsi->half_repeat_len * 2);
          }
        }
      }
    }
  }

  if (num_pt_samples == 0) {
    fprintf(stderr, "Cannot create output file containing no samples!\n");
    return NULL;
  }
  assert(pt_samples != NULL);

  *num_samples_out = num_pt_samples;

  return pt_samples;

cleanup:
  free(pt_samples);
  return NULL;
}

static bool glissando_machine(unsigned int  flags,
                              ChannelState  channels[NUM_PT_CHANNELS],
                              unsigned int  c,
                              FILE         *f)
{
  assert(channels != NULL);
  assert(c < NUM_SF_CHANNELS);
  assert(f != NULL);

  switch (channels[c].glissando_state) {
    case GlissandoState_Wait:
      DEBUG_OUT("Paused glissando on channel %u\n", c + 1);
      assert((flags & FLAGS_GLISSANDO_FAST) == 0);
      channels[c].glissando_state = GlissandoState_Continue;
      /* fallthrough */

    case GlissandoState_None:
      /* No glissando on this channel yet */
      return fput_pt_command(PT_COM_NORMAL, 0, 0, 0, f);

    case GlissandoState_Start:
      /* Tell the player the target pitch and sample number only at
         the start of the glissando. */
      DEBUG_OUT("Starting glissando of sample %u to pitch %u on "
                "channel %u\n", channels[c].pt_sample_no,
                channels[c].target_pitch, c + 1);

      /* Interpolate Tone Portamento commands with Normal Play commands
         (standard glissando is too fast). */
      channels[c].glissando_state = (flags & FLAGS_GLISSANDO_FAST) == 0 ?
                                    GlissandoState_Wait :
                                    GlissandoState_Continue;

      return fput_pt_command(PT_COM_TONE_PORTAMENTO,
                             1, /* Too fast for Star Fighter 3000 music */
                             channels[c].pt_sample_no,
                             channels[c].target_pitch,
                             f);

    case GlissandoState_Continue:
      DEBUG_OUT("Continuing glissando on channel %u\n", c + 1);

      /* Interpolate Tone Portamento commands with Normal Play commands
         (standard glissando is too fast). */
      channels[c].glissando_state = (flags & FLAGS_GLISSANDO_FAST) == 0 ?
                                    GlissandoState_Wait :
                                    GlissandoState_Continue;

      return fput_pt_command(PT_COM_TONE_PORTAMENTO,
                             1, /* Too fast for Star Fighter 3000 music */
                             0, /* Don't like but Martin says it's correct */
                             0, /* Don't like but Martin says it's correct */
                             f);

    default:
      assert("Bad glissando state" == NULL);
      return false; /* failure */
  }
}

static void warn_octave(signed int    octave,
                        unsigned int  channel,
                        unsigned int  division_no,
                        int_least32_t pattern_no)
{
  if (octave < 1 || octave > PT_OCTAVE_RANGE - 2) {
    printf("Utilising non-standard octave %d on channel %u (division %u of "
           "pattern %"PRIdLEAST32")\n",
           octave,
           channel + 1,
           division_no,
           pattern_no);
  }
}

static bool transcode_patterns(unsigned int        flags,
                               const void         *music_data,
                               const PTSampleInfo *pt_samples,
                               unsigned int        num_pt_samples,
                               const SampleInfo   *samples_index,
                               unsigned int        nsamp,
                               unsigned int        last_play,
                               FILE               *f)
{
  const unsigned char *voice_table;
  const SFPattern *patterns;
  int_least32_t pattern_no, last_pattern_no;
  ChannelState channels[NUM_PT_CHANNELS], final_channels[NUM_PT_CHANNELS];

  assert(music_data != NULL);
  assert(pt_samples != NULL);
  assert(samples_index != NULL);
  assert(f != NULL);

  voice_table = (const unsigned char *)music_data + 16;
  patterns = (const SFPattern *)((char *)music_data + 104);
  last_pattern_no = read_int32le((char *)music_data + 32);

  /* An extra pattern may be required to allow late notes to finish. */
  if ((flags & FLAGS_BLANK_PATTERN) != 0)
    last_pattern_no ++;

  for (pattern_no = 0; pattern_no <= last_pattern_no; pattern_no++)
  {
    const SFPattern *pattern;
    unsigned int division_no;

    if ((flags & FLAGS_BLANK_PATTERN) != 0 && pattern_no == last_pattern_no) {
      if ((flags & FLAGS_VERBOSE) != 0)
        puts("About to write a blank ProTracker pattern");

      /* We are appending a blank pattern so restore the state of the channels
         at the end of the pattern played immediately beforehand, to allow
         continuation of any glissando effects. */
      pattern = NULL;
      memcpy(&channels, &final_channels, sizeof(channels));
    } else {
      /* Clear the state of every channel at the start of each new pattern. This
         isn't strictly accurate, but it's the best that we can practically do
         given that patterns may be played in any order. */
      unsigned int c;

      if ((flags & FLAGS_VERBOSE) != 0)
        printf("About to transcode pattern %"PRIdLEAST32"\n", pattern_no);

      for (c = 0; c < NUM_PT_CHANNELS; c++) {
        channels[c].glissando_state = GlissandoState_None;
        channels[c].sample_num = UINT_MAX;
      }
      pattern = patterns + pattern_no;
    }

    for (division_no = 0; division_no < NUM_SF_DIVISIONS; division_no++)
    {
      unsigned int c;
      const SFDivision *division;
      static const SFDivision blank_division = {{{0,0,0,0},
                                                 {0,0,0,0},
                                                 {0,0,0,0},
                                                 {0,0,0,0}}};

      if (pattern == NULL)
        division = &blank_division;
      else
        division = &pattern->divisions[division_no];

      /* First examine the command for each channel to discover any glissando
         effects that should be applied to all channels. */
      assert(NUM_PT_CHANNELS <= NUM_SF_CHANNELS);
      for (c = 0; c < NUM_PT_CHANNELS; c++) {
        const SFChannelData *com = &division->channels[c];
        unsigned int c2, sample_num, note;
        signed long pt_tuning;
        signed int octave;

        /* Is this a glissando effect? */
        if (com->voice_act >> 4 < SF_GLISSANDO_THRESHOLD)
          continue; /* no */

        sample_num = voice_table[com->voice_act & 0xf];
        if (sample_num >= nsamp ||
            (samples_index + sample_num)->type == SampleInfo_Type_Unused)
          continue;

        /* Convert the SF3000 octave and note numbers into ProTracker
           equivalents. */
        pt_tuning = sf_to_pt_tuning((samples_index + sample_num)->tuning);
        octave = note_to_pt(com, &note, pt_tuning / PT_TUNING_SEMITONE);

        /* A quirk is that a glissando affects all instances of the specified
           sample - regardless of which channel it is playing on. */
        for (c2 = 0; c2 < NUM_SF_CHANNELS; c2++) {
          const PTSampleInfo *ptsi;
          signed int min_octave, max_octave, chan_octave;

          if (channels[c2].sample_num != sample_num)
            continue;

          if (c2 != c) {
            if ((flags & FLAGS_VERBOSE) != 0)
              printf("Glissando on channel %u->%u is %s (division %u of "
                     "pattern %"PRIdLEAST32")\n", c + 1, c2 + 1,
                     (flags & FLAGS_GLISSANDO_SINGLE) == 0 ?
                     "allowed" : "forbidden", division_no, pattern_no);

            if ((flags & FLAGS_GLISSANDO_SINGLE) != 0)
              continue;
          }

          /* Get a pointer to the ProTracker sample information */
          assert(channels[c2].pt_sample_no > 0);
          assert(channels[c2].pt_sample_no <= num_pt_samples);
          ptsi = pt_samples + channels[c2].pt_sample_no - 1;

          /* Make the target pitch specific to the variation of the sample
             playing on this channel (which may have been pre-tuned to a
             different octave). */
          if (ptsi->octaves_cheat != 0) {
            DEBUG_OUT("Glissando of pre-tuned sample (by %d octaves)\n",
                      ptsi->octaves_cheat);
          }
          chan_octave = octave - ptsi->octaves_cheat;
          /* e.g. Use octave 1 to obtain octave 0 with a sample pre-tuned 'up'
                  by -1 octave. */

          /* ProTracker octaves 0 and 4 are non-standard and may not be
             available. */
          if ((flags & FLAGS_EXTRA_OCTAVES) == 0) {
            min_octave = 1;
            max_octave = PT_OCTAVE_RANGE - 2;
          } else {
            min_octave = 0;
            max_octave = PT_OCTAVE_RANGE - 1;
          }
          if (chan_octave < min_octave || chan_octave > max_octave) {
            if (chan_octave < min_octave)
              chan_octave = min_octave;
            else if (chan_octave > max_octave)
              chan_octave = max_octave;

            fprintf(stderr, "Warning: target for glissando out of range "
                    "on channel %u (division %u of pattern %"PRIdLEAST32")\n",
                    c2 + 1, division_no, pattern_no);
          }

          if ((flags & FLAGS_VERBOSE) != 0)
            warn_octave(chan_octave, c2, division_no, pattern_no);

          if (channels[c2].glissando_state != GlissandoState_None) {
            DEBUG_OUT("New glissando cancels existing glissando of "
                      "sample %u to pitch %u on channel %u\n",
                      channels[c2].pt_sample_no, channels[c2].target_pitch,
                      c2 + 1);
          }
          /* Schedule an immediate Tone Portamento command */
          channels[c2].target_pitch = get_pt_period(chan_octave, note);
          channels[c2].glissando_state = GlissandoState_Start;

          DEBUG_OUT("%u: New glissando of sample %u to pitch %u on "
                    "channel %u\n", (char *)com - (char *)music_data,
                    channels[c2].pt_sample_no, channels[c2].target_pitch,
                    c2 + 1);
        }
      }

      assert(NUM_PT_CHANNELS <= NUM_SF_CHANNELS);
      for (c = 0; c < NUM_PT_CHANNELS; c++) {
        const SFChannelData *com = &division->channels[c];
        unsigned int sample_num, octave, note, pt_sample_no;
        signed int octaves_cheat;
        const SampleInfo *sample;

        if (!command(com)) {
no_command:
          /* We may need to output a Tone Portamento command to continue a
             glissando. */
          if (!glissando_machine(flags, channels, c, f))
            return false;
          else
            continue;
        }

        if (com->voice_act >> 4 >= SF_GLISSANDO_THRESHOLD)
          goto no_command; /* dealt with glissando starts on first pass */

        sample_num = voice_table[com->voice_act & 0xf];
        if (sample_num >= nsamp)
          goto no_command; /* undefined sample */

        sample = samples_index + sample_num;
        switch (sample->type) {
          case SampleInfo_Type_Unused:
            goto no_command; /* undefined sample */

          case SampleInfo_Type_Effect:
            if ((flags & FLAGS_ALLOW_SFX) == 0)
              goto no_command; /* Sound effects not allowed during music */
            break;

          default:
            assert(sample->type == SampleInfo_Type_Music);
            break;
        }

        /* Convert the SF3000 octave and note numbers into ProTracker
           equivalents. */
        octaves_cheat = calc_octaves_cheat(flags,
                                           com,
                                           sf_to_pt_tuning(sample->tuning),
                                           &octave,
                                           &note);

        if ((flags & FLAGS_VERBOSE) != 0)
          warn_octave(octave, c, division_no, pattern_no);

        /* Search for the variation of the sample with the appropriate number
           of repeats. */
        pt_sample_no = find_pt_sample(pt_samples,
                                      num_pt_samples,
                                      com->num_repeats >> 4,
                                      sample_num,
                                      octaves_cheat);
        assert(pt_sample_no != 0);

        if (!fput_pt_command(PT_COM_SET_VOLUME,
                             (com->oct_vol >> 4) *
                               PT_MAX_VOLUME / SF_MAX_VOLUME,
                             pt_sample_no,
                             get_pt_period(octave, note),
                             f))
          return false; /* failure */

        channels[c].sample_num = sample_num;
        channels[c].pt_sample_no = pt_sample_no;

        if (channels[c].glissando_state != GlissandoState_None) {
          DEBUG_OUT("New note cancels glissando of sample %u to pitch %u "
                    "on channel %u\n", channels[c].pt_sample_no,
                    channels[c].target_pitch, c + 1);
        }

        channels[c].glissando_state = GlissandoState_None;
      }
    }

    /* If we just transcoded the pattern to be played last then copy the state
       of the channels to allow continuation of any glissando effects on the
       additional 'blank' pattern (if one is to be appended). */
    if ((flags & FLAGS_BLANK_PATTERN) != 0 && pattern_no == last_play) {
      DEBUG_OUT("Retaining channels state at end of pattern %"PRIdLEAST32"\n",
                pattern_no);

      memcpy(&final_channels, &channels, sizeof(final_channels));
    }
  }
  return true; /* success */
}

bool check_tuning(signed int sf_tuning)
{
  const int pt_octave = PT_TUNING_SEMITONE * SEMITONES_PER_OCTAVE;

  /* Check that a long integer will accommodate the conversion from SF3000
     tuning units to ProTracker tuning units. */
  return ((int)abs(sf_tuning) <= (LONG_MAX - SF_TUNING_OCTAVE / 2) / pt_octave);
}

bool create_protracker(unsigned int      flags,
                       const char       *music_file,
                       const void       *music_data,
                       const char       *samples_dir,
                       const SampleInfo *samples_index,
                       unsigned int      nsamp,
                       const char       *output_file)
{
  FILE *f = NULL;
  char name[20];
  unsigned int num_pt_samples, last_play, speed;
  PTSampleInfo *pt_samples = NULL;

  assert(music_file != NULL);
  assert(music_data != NULL);
  assert(samples_dir != NULL);
  assert(samples_index != NULL || nsamp == 0);
  assert(output_file != NULL);

  /* First pass is to determine which samples (and variants thereof) to
     include in the ProTracker file. */
  pt_samples = make_pt_sample_list(flags,
                                   music_data,
                                   samples_index,
                                   nsamp,
                                   &num_pt_samples);
  if (pt_samples == NULL)
    goto cleanup;

  /* Open the output file */
  if ((flags & FLAGS_VERBOSE) != 0)
    printf("Opening output file '%s'\n", output_file);

  f = fopen(output_file, "wb");
  if (f == NULL) {
    fprintf(stderr,"Failed to open output file '%s'\n", output_file);
    goto cleanup;
  }

  /* Use the leaf part of the input file path as the song name */
  memset(name, '\0', sizeof(name));
  strncpy(name, find_leaf(music_file), sizeof(name)-1);
  if ((flags & FLAGS_VERBOSE) != 0)
    printf("Writing ProTracker song name '%s'\n", name);

  fwrite(name, sizeof(name), 1, f);

  /* Write sample info */
  if (!write_sample_table(flags, pt_samples, num_pt_samples, samples_index, f))
    goto cleanup_write;

  /* Write the order in which to play patterns and get the number of the
     pattern to be played last. */
  if (!write_play_order(flags, output_file, music_data, &last_play, f))
    goto cleanup;

  /* Write Mahoney & Kaktus identifier to indicate that the ProTracker file
     may include 31 rather than 15 samples. */
  if (fputs("M.K.", f) == EOF)
    goto cleanup_write;

  /* First byte of Star Fighter 3000 music data gives the tempo as an interval
     between divisions (in centiseconds). */
  speed = *(unsigned char *)music_data;
  if ((flags & FLAGS_VERBOSE) != 0)
    printf("SF3000 music tempo is %u cs\n", speed);

  if (speed >= PT_SPEED_THRESHOLD) {
    fprintf(stderr, "Tempo %u is too slow in input file (limit is %u)\n",
                    speed, PT_SPEED_THRESHOLD - 1);
    goto cleanup;
  }

  /* Write data for pattern 0, which will set the tempo for the song... */
  if (!write_tempo_pattern(flags, speed, f))
    goto cleanup_write;

  /* Second pass is to transcode the command data from SF3000 to ProTracker
     format. */
  if (!transcode_patterns(flags,
                          music_data,
                          pt_samples,
                          num_pt_samples,
                          samples_index,
                          nsamp,
                          last_play,
                          f))
    goto cleanup_write;

  /* Store the sound samples right after the pattern data. */
  if (!integrate_samples(flags,
                         pt_samples,
                         num_pt_samples,
                         samples_index,
                         samples_dir,
                         output_file,
                         f))
    goto cleanup;

  free(pt_samples);

  if ((flags & FLAGS_VERBOSE) != 0)
    puts("Closing output file");

  fclose(f);

  return true; /* success */

cleanup_write:
  fprintf(stderr, "Failed writing to output file '%s'\n", output_file);

cleanup:
  free(pt_samples);
  if (f != NULL) {
    if ((flags & FLAGS_VERBOSE) != 0)
      puts("Closing output file");

    fclose(f);
  }

  return false; /* failure */
}
